#ifndef __MY_SERVICE__
#define __MY_SERVICE__

#include "data_manage.hpp"
#include "httplib.h"

extern cloud::DataManager *_data;
namespace cloud
{
    class Service
    {
    public:
        Service()
        {
            Config *config = Config::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
        }
        ~Service()
        {
        }
        bool RunModule()
        {
            _server.Post("/upload", Upload);
            _server.Get("/listshow/", ListShow);
            _server.Get("/", ListShow);
            std::string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, Download);
            // _server.Get("/download/(.*)", Download);

            _server.listen(_server_ip.c_str(), _server_port);

            return true;
        }

    private:
        // 上传处理
        static void Upload(const httplib::Request &req, httplib::Response &rsp)
        {
            // post /upload
            // 判断有没有上传的文件区域
            auto ret = req.has_file("file");
            if (ret == false)
            {
                std::cout << "not file upload\n";
                rsp.status = 400;
                return;
            }
            // 获取文件属性和文件内容等信息
            const auto &file = req.get_file_value("file");
            // file.filename:文件名     file.content:文件内容
            std::string back_dir = Config::GetInstance()->GetBackDir();
            std::string realpath = back_dir + filemanage(file.filename).FileName();
            // 向文件写入内容
            filemanage fm(realpath);
            fm.SetContent(file.content);
            BackupInfo bp;
            // 组织备份的文件信息
            bp.NewBackupInfo(realpath);
            // 向数据管理模块添加备份的文件信息
            _data->Insert(bp);

            return;
        }
        // 展示页面
        static void ListShow(const httplib::Request &req, httplib::Response &rsp)
        {
            // 1、获取所有的文件备份信息
            std::vector<BackupInfo> vb;
            _data->GetAll(vb);

            // 2、根据所有的备份信息，组织html文件数据
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for (auto &data : vb)
            {
                ss << "<tr>";
                std::string filename = filemanage(data._realpath).FileName();
                ss << "<td><a href=\"" << data._url << "\">" << filename << "</a></td>";        // 由这里确定文件的入口:通过data.url定位资源./download/文件名
                ss << "<td align=\"right\">" << TimetoStr(data._atime) << "</td>";
                ss << "<td align=\"right\">" << data._fsize / 1024 << "k</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;

            return;
        }
        // 下载处理
        static void Download(const httplib::Request &req, httplib::Response &rsp)
        {
            // 1、获取客户端请求的资源路径path
            // 2、根据资源路径，获取文件备份信息
            BackupInfo info;
            _data->GetOneByUrl(req.path, info);
            // 3、判断文件是否被压缩，如果被压缩，要先解压缩
            if (info._packflag == true)
            {
                filemanage fm(info._packpath);
                fm.UnCompress(info._realpath);

                // 4、删除压缩包，修改备份信息(已经没有被压缩)
                fm.Romove();
                info._packflag = false;
                _data->Update(info);
            }
            // 5、读取文件数据，放入响应正文
            filemanage fm(info._realpath);
            fm.GetContent(rsp.body);

            // 6、设置响应头部字段：ETag, Accept-Ranges: bytes
            // Accept-Ranges: bytes，表示支持断点续传功能
            // httplib内部实现了对于区间请求也就是断点续传请求的处理
            // 只需要用户将文件所有数据读取到响应正文中，它内部会自动根据请求区间
            // 从正文中取出指定区间数据进行响应
            rsp.set_header("Accept-Ranges", "bytes");
            // ETag:"fasdfasdf-表示文件唯一标识，客户端收到响应会保存这个信息，用于服务器判断这个文件与原先下载的文件是否一致
            rsp.set_header("ETag", GetETag(info));
            rsp.set_header("Content-Type", "application/octet-stream");
            // 内部实现了断点续传，因此状态码是200还是206，无需用户设置
            // rsp.status = 200;

            return;
        }
        // 转换为年月日
        static std::string TimetoStr(time_t t)
        {
            std::string tmp = std::ctime(&t);
            return tmp;
        }
        // 获取ETag
        static std::string GetETag(const BackupInfo &info)
        {
            // etag: filename-fsize-mtime
            filemanage fm(info._realpath);
            std::string etag = fm.FileName();
            etag += '-';
            etag += std::to_string(info._fsize);
            etag += '-';
            etag += std::to_string(info._mtime);

            return etag;
        }

    private:
        std::string _server_ip;
        int _server_port;
        std::string _download_prefix;
        httplib::Server _server;
    };
}
#endif